#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
//#include <config.h>
//#include <disp_manager.h>
#include <linux/fb.h>
#include <sys/mman.h>

#include <config.h>
#include <font_manager.h>
#include <disp_manager.h>
#include <encoding_manager.h>
#include <input_manager.h>

static int FBShowPixel(int , int, unsigned int);
static int FBClearScreen(unsigned int);
static int FBDeviceInit(void);

static int g_iFBFD;
static struct fb_var_screeninfo g_tVar;	/* Current var */
static struct fb_fix_screeninfo g_tFix;	/* Current fix */
static int g_iScreenSize;
static int g_iLineWidth;
static int g_iPixelWidth;

static T_DispOpr g_tFBDispOpr = 
{
	.name		= "fb",
	.ucpDispMem  = NULL,
	.DeviceInit	= FBDeviceInit,
	.ShowPixel	= FBShowPixel,
	.ClearScreen	= FBClearScreen,
};

static int FBDeviceInit(void)
{
	g_iFBFD = open(FB_DEVICE_NAME, O_RDWR);
	if (g_iFBFD < 0)
	{
		DBG_PRINTF("can't open /dev/fb0\n");
		return -1;
	}

	if (ioctl(g_iFBFD, FBIOGET_VSCREENINFO, &g_tVar))
	{
		DBG_PRINTF("can't get var\n");
		return -1;
	}

	if (ioctl(g_iFBFD, FBIOGET_FSCREENINFO, &g_tFix))
	{
		DBG_PRINTF("can't get fix\n");
		return -1;
	}

	//g_tFBDispOpr.name = FB_DEVICE_NAME;
	g_tFBDispOpr.iXres = g_tVar.xres;
	g_tFBDispOpr.iYres = g_tVar.yres;
	g_tFBDispOpr.iBpp = g_tVar.bits_per_pixel;
	g_iScreenSize = g_tFBDispOpr.iXres * g_tFBDispOpr.iYres * g_tFBDispOpr.iBpp / 8;

	g_iLineWidth = g_tFBDispOpr.iXres * g_tFBDispOpr.iBpp / 8;
	g_iPixelWidth = g_tFBDispOpr.iBpp / 8;
	
	g_tFBDispOpr.ucpDispMem = (unsigned char *)mmap(NULL , g_iScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_iFBFD, 0);
	if (g_tFBDispOpr.ucpDispMem  == (unsigned char *)-1)
	{
		DBG_PRINTF("can't mmap\n");
		return -1;
	}

	return 0;
}

/* dwColor = 0xRRGGBB */
static int FBShowPixel(int iPenX, int iPenY, unsigned int dwColor)
{
	unsigned char *pucPen_8 = g_tFBDispOpr.ucpDispMem + iPenY * g_iLineWidth + iPenX * g_iPixelWidth;
	unsigned short *pwPen_16;	
	unsigned int *pdwPen_32;	

	unsigned int dwRed, dwGreen, dwBlue;	

	pwPen_16 = (unsigned short *)pucPen_8;
	pdwPen_32 = (unsigned int *)pucPen_8;

	if(dwColor == NOTCHANGE)
		return 0;

	switch (g_tFBDispOpr.iBpp)
	{
		case 8:
		{
			*pucPen_8 = dwColor;
			break;
		}
		case 16:
		{
			/* 565 */
			dwRed   	= (dwColor >> 16) & 0xff;
			dwGreen 	= (dwColor >> 8) & 0xff;
			dwBlue  	= (dwColor >> 0) & 0xff;
			dwColor = ((dwRed >> 3) << 11) | ((dwGreen >> 2) << 5) | (dwBlue >> 3);
			*pwPen_16 = dwColor;
			break;
		}
		case 32:
		{
			*pdwPen_32 = dwColor;
			break;
		}
		default:
		{
			DBG_PRINTF("can't surport %dbpp\n", g_tFBDispOpr.iBpp);
			return -1;
			break;
		}
	}
	return 0;
}

static int FBClearScreen(unsigned int dwBackColor)
{
	unsigned int dwRed, dwGreen, dwBlue;
	unsigned short *pwPen_16;
	unsigned int *pwPen_32;
	int i;

	pwPen_16 = (unsigned short *)g_tFBDispOpr.ucpDispMem;
	pwPen_32 = (unsigned int *)g_tFBDispOpr.ucpDispMem;

	switch (g_tFBDispOpr.iBpp)
		{
			case 8:
			{
				memset(g_tFBDispOpr.ucpDispMem, dwBackColor, g_iScreenSize);
				break;
			}
			case 16:
			{
				/* 565 */
				dwRed		= (dwBackColor >> 16) & 0xff;
				dwGreen 	= (dwBackColor >> 8) & 0xff;
				dwBlue		= (dwBackColor >> 0) & 0xff;
				dwBackColor = ((dwRed >> 3) << 11) | ((dwGreen >> 2) << 5) | (dwBlue >> 3);
				for(i = 0; i < g_iScreenSize; i += 2)
				{
					*pwPen_16++ = dwBackColor;
				}
				break;
			}
			case 32:
			{
				for(i = 0; i < g_iScreenSize; i += 4)
				{
					*pwPen_32++ = dwBackColor;
				}
				break;
			}
			default:
			{
				DBG_PRINTF("can't surport %dbpp\n", g_tFBDispOpr.iBpp);
				return -1;
				break;
			}
		}
		return 0;

}

int FBRegister(void)
{
	RegisterDispOpr(&g_tFBDispOpr);
	return 0;
}
